using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;

namespace SMS.WinForms.Controls
{
    /// <summary>
    /// Defines the delegate used for the <see cref="SMS.WinForms.Controls.WizardPageCollection.PageAdded"/> event.
    /// </summary>
    /// <param name="sender">The object that raised the event.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void WizardPageCollectionPageAddedDelegate(Object sender, WizardPageCollectionEventArgs e);

    /// <summary>
    /// Defines the delegate used for the <see cref="SMS.WinForms.Controls.WizardPageCollection.PageRemoved"/> event.
    /// </summary>
    /// <param name="sender">The object that raised the event.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void WizardPageCollectionPageRemovedDelegate(Object sender, WizardPageCollectionEventArgs e);

    /// <summary>
    /// The <see cref="SMS.WinForms.Controls.WizardPageCollection"/> class contains a set of
    /// <see cref="SMS.WinForms.Controls.WizardPage"/> controls that are loaded by a
    /// <see cref="SMS.WinForms.WizardForm"/> window.
    /// </summary>
    public class WizardPageCollection : IDictionary<String, WizardPage>
    {
        #region Declarations

        /// <summary>
        /// Stores the collection of pages.
        /// </summary>
        private Dictionary<String, WizardPage> _store = null;

        /// <summary>
        /// Raised when a <see cref="SMS.WinForms.Controls.WizardPage"/> control is added to the
        /// <see cref="SMS.WinForms.Controls.WizardPageCollection"/> class.
        /// </summary>
        public event WizardPageCollectionPageAddedDelegate PageAdded;

        /// <summary>
        /// Raised when a <see cref="SMS.WinForms.Controls.WizardPage"/> control is removed from the
        /// <see cref="SMS.WinForms.Controls.WizardPageCollection"/> class.
        /// </summary>
        public event WizardPageCollectionPageRemovedDelegate PageRemoved;
        
        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SMS.WinForms.Controls.WizardPageCollection"/> class.
        /// </summary>
        public WizardPageCollection()
        {
            // Create the dictionary
            _store = new Dictionary<string, WizardPage>();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Raises the <see cref="E:PageAdded"/> event.
        /// </summary>
        /// <param name="e">The <see cref="SMS.WinForms.Controls.WizardPageCollectionEventArgs"/> instance containing the event data.</param>
        public void OnPageAdded(WizardPageCollectionEventArgs e)
        {
            // Check if the event is handled
            if (PageAdded != null)
            {
                // Raise the event
                PageAdded(this, e);
            }
        }

        /// <summary>
        /// Raises the <see cref="E:PageRemoved"/> event.
        /// </summary>
        /// <param name="e">The <see cref="SMS.WinForms.Controls.WizardPageCollectionEventArgs"/> instance containing the event data.</param>
        public void OnPageRemoved(WizardPageCollectionEventArgs e)
        {
            // Check if the event is handled
            if (PageRemoved != null)
            {
                // Raise the event
                PageRemoved(this, e);
            }
        }

        /// <summary>
        /// Adds an element with the provided key and value to the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </summary>
        /// <param name="key">The object to use as the key of the element to add.</param>
        /// <param name="value">The object to use as the value of the element to add.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"></see> is read-only.</exception>
        /// <exception cref="T:System.ArgumentException">An element with the same key already exists in the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.</exception>
        /// <exception cref="T:System.ArgumentNullException">key is null.</exception>
        public void Add(String key, WizardPage value)
        {
            // Check if there is a key value
            if (String.IsNullOrEmpty(key) == true)
            {
                throw new ArgumentException("key");
            }

            // Check if there is a page
            if (value == null)
            {
                throw new ArgumentException("value");
            }

            // Add the page to the store
            _store.Add(key, value);

            // Raise the event
            OnPageAdded(new WizardPageCollectionEventArgs(key, value));
        }

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
        public void Add(KeyValuePair<String, WizardPage> item)
        {
            Add(item.Key, item.Value);
        }

        /// <summary>
        /// Adds the specified page.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="page">The page.</param>
        /// <param name="settings">The settings.</param>
        public void Add(String key, WizardPage page, WizardPageSettings settings)
        {
            // Add the settings to the page
            page.PageSettings = settings;

            // Add the page to the collection
            Add(key, page);
        }

        /// <summary>
        /// Adds the specified page.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="page">The page.</param>
        /// <param name="settings">The settings.</param>
        /// <param name="navigationSettings">The navigation settings.</param>
        public void Add(String key, WizardPage page, WizardPageSettings settings, WizardPageNavigationSettings navigationSettings)
        {
            // Add the settings to the page
            page.PageSettings = settings;
            page.NavigationSettings = navigationSettings;

            // Add the page to the collection
            Add(key, page);
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.IDictionary`2"></see> contains an element with the specified key.
        /// </summary>
        /// <param name="key">The key to locate in the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.</param>
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.IDictionary`2"></see> contains an element with the key; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.ArgumentNullException">key is null.</exception>
        public bool ContainsKey(String key)
        {
            return _store.ContainsKey(key);
        }

        /// <summary>
        /// Determines whether the specified value contains value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>
        /// 	<item>true</item> if the specified value contains value; otherwise, <item>false</item>.
        /// </returns>
        public bool ContainsValue(WizardPage value)
        {
            return _store.ContainsValue(value);
        }

        /// <summary>
        /// Removes the element with the specified key from the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <returns>
        /// true if the element is successfully removed; otherwise, false.  This method also returns false if key was not found in the original <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"></see> is read-only.</exception>
        /// <exception cref="T:System.ArgumentNullException">key is null.</exception>
        public bool Remove(String key)
        {
            // Get the page from the store
            WizardPage page = _store[key];

            // Remove the page
            Boolean returnValue = _store.Remove(key);

            // Check if there are values for the event
            if ((String.IsNullOrEmpty(key) == false)
                && (page != null))
            {
                // Raise the event
                OnPageRemoved(new WizardPageCollectionEventArgs(key, page));
            }

            // Return whether the remove was successful
            return returnValue;
        }

        /// <summary>
        /// Tries the get value.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public bool TryGetValue(String key, out WizardPage value)
        {
            return _store.TryGetValue(key, out value);
        }

        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only. </exception>
        public void Clear()
        {
            _store.Clear();
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> contains a specific value.
        /// </summary>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        /// <returns>
        /// true if item is found in the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false.
        /// </returns>
        public bool Contains(KeyValuePair<String, WizardPage> item)
        {
            return ((_store.ContainsKey(item.Key) == true) && (_store.ContainsValue(item.Value) == true));
        }

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"></see> to an <see cref="T:System.Array"></see>, starting at a particular <see cref="T:System.Array"></see> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"></see>. The <see cref="T:System.Array"></see> must have zero-based indexing.</param>
        /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">arrayIndex is less than 0.</exception>
        /// <exception cref="T:System.ArgumentNullException">array is null.</exception>
        /// <exception cref="T:System.ArgumentException">array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"></see> is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array.</exception>
        public void CopyTo(KeyValuePair<String, WizardPage>[] array, int arrayIndex)
        {
            // Loop through each page
            foreach (String key in Keys)
            {
                // Get the page for the key
                WizardPage page = this[key];

                // Create the pair for the array
                KeyValuePair<String, WizardPage> pair = new KeyValuePair<string, WizardPage>(key, page);

                // Add the pair to the array
                array[arrayIndex] = pair;

                // Increment the index
                arrayIndex++;
            }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        /// <returns>
        /// true if item was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false. This method also returns false if item is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
        public bool Remove(KeyValuePair<String, WizardPage> item)
        {
            // Check if there is a key value supplied
            if (String.IsNullOrEmpty(item.Key) == true)
            {
                throw new ArgumentNullException("item.Key");
            }

            return Remove(item.Key);
        }

        /// <summary>
        /// Gets the next page from the collection.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <returns>A <see cref="SMS.WinForms.Controls.WizardPage"/> instance. Returns null if no relevant page exists.</returns>
        public WizardPage GetNextStoredPage(WizardPage page)
        {
            WizardPage previousItem = null;

            // Loop through each page
            foreach (WizardPage item in Values)
            {
                // Check if there isn't a page specified
                if (page == null)
                {
                    // Return the first page encountered
                    return item;
                }

                // Check if there is a previous item to check and that it matches the page being searched for
                if ((previousItem != null)
                    && (previousItem.Equals(page) == true))
                {
                    // Return the current item
                    return item;
                }

                // Store the current item for the next iteration
                previousItem = item;
            }

            // The page wasn't found
            return null;
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<KeyValuePair<String, WizardPage>> GetEnumerator()
        {
            return _store.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return _store.GetEnumerator();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"></see> containing the keys of the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </summary>
        /// <value></value>
        /// <returns>An <see cref="T:System.Collections.Generic.ICollection`1"></see> containing the keys of the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"></see>.</returns>
        public ICollection<String> Keys
        {
            get
            {
                return _store.Keys;
            }
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"></see> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </summary>
        /// <value></value>
        /// <returns>An <see cref="T:System.Collections.Generic.ICollection`1"></see> containing the values in the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"></see>.</returns>
        public ICollection<WizardPage> Values
        {
            get
            {
                return _store.Values;
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="SMS.WinForms.Controls.WizardPage"/> with the specified key.
        /// </summary>
        /// <value></value>
        public WizardPage this[String key]
        {
            get
            {
                return _store[key];
            }
            set
            {
                _store[key] = value;
            }
        }

        /// <summary>
        /// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <value></value>
        /// <returns>The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</returns>
        public int Count
        {
            get
            {
                return _store.Count;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only; otherwise, false.</returns>
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        #endregion
    }
}
